/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.bumptech.glide.load.resource.gif.drawableability;


import com.bumptech.glide.util.LogUtil;
import com.bumptech.glide.util.Synthetic;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.Image;
import ohos.agp.components.element.Element;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.PixelMapHolder;
import ohos.agp.render.Texture;
import ohos.agp.utils.Matrix;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.global.resource.NotExistException;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Size;
import ohos.multimodalinput.event.TouchEvent;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * 绘制GIF DraweeView
 */
public class DraweeView extends Image implements Component.BindStateChangedListener, Callback {
    private final String TAG = DraweeView.class.getSimpleName();
    private static final Lock BITMAP_DRAWABLE_LOCK = new NoLock();

    public DraweeView(Context context) {
        super(context);
        init(context);
    }

    public DraweeView(Context context, AttrSet attrs) {
        super(context, attrs);
        init(context);
    }

    public DraweeView(Context context, AttrSet attrs, String defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }


    /**
     * This method is idempotent so it only has effect the first time it's called
     */
    private void init(Context context) {
        setBindStateChangedListener(this);
        addDrawTask(this::drawToCanvas);
        setTouchEventListener(this::onTouchEvent);
    }


    public void onChange(Element element) {
        invalidate();
    }

    @Override
    public void setLayoutConfig(ComponentContainer.LayoutConfig config) {
        super.setLayoutConfig(config);
        invalidate();
    }


    @Override
    public void setImageElement(Element element) {
        if (element == null) {
            //如果设置的内容为null 则去刷新图片并且清空之前的东西
            invalidate();
            return;
        }
        super.setImageElement(element);
        element.setCallback(this::onChange);
        if (element instanceof RootShapeElement) {
            LogUtil.error(TAG, "setImageElement set hmcallback");
            ((RootShapeElement) element).setHmCallback(this);
        }
    }

    private static final class NoLock implements Lock {

        @Synthetic
        NoLock() {}

        @Override
        public void lock() {
            // do nothing
        }

        @Override
        public void lockInterruptibly() throws InterruptedException {
            // do nothing
        }

        @Override
        public boolean tryLock() {
            return true;
        }

        @Override
        public boolean tryLock(long time, @NotNull TimeUnit unit) throws InterruptedException {
            return true;
        }

        @Override
        public void unlock() {
            // do nothing
        }

        @NotNull
        @Override
        public Condition newCondition() {
            throw new UnsupportedOperationException("Should not be called");
        }
    }

    private void drawToCanvas(Component component, Canvas canvas) {
        // LogUtil.info("dogif", "drawToCanvas is go, getImageElement() is=" + (getImageElement() != null));
        if (getImageElement() == null) {
            //Element 没有设置 则不绘制任何内容
            return;
        }
        if (getImageElement() instanceof RootShapeElement) {
            RootShapeElement rootShapeElement = (RootShapeElement) getImageElement();
            int rw;
            int rh;
            int cw;
            int ch;
            rw = rootShapeElement.getIntrinsicWidth();
            rh = rootShapeElement.getIntrinsicHeight();
            cw = component.getWidth();
            ch = component.getHeight();
            // LogUtil.info("doGif", "rootShapeElement w =" + rootShapeElement.getIntrinsicWidth() +
            // " rootShapeElement h =" + rootShapeElement.getIntrinsicHeight() +
            // " Component w =" + component.getWidth() +
            // " Component h =" + component.getHeight()
            // );

            PixelMap.InitializationOptions opts = new PixelMap.InitializationOptions();
            opts.size = new Size(rw, rh);
            opts.pixelFormat = PixelFormat.ARGB_8888;
            opts.editable = true;
            PixelMap gifmap = PixelMap.create(opts);
            applyDrawToCanvas(gifmap);

            RectFloat src = new RectFloat(0, 0, rw, rh);
            RectFloat dst = scaleTypeFixed(gifmap, component);

            PixelMapHolder pixelMapHolder = new PixelMapHolder(gifmap);
            canvas.drawPixelMapHolderRect(pixelMapHolder, src, dst, getGifDrawPaint());
        }
    }

    private Paint gifPaint;

    private Paint getGifDrawPaint() {
        if (gifPaint == null) {
            gifPaint = new Paint();
            gifPaint.setAntiAlias(true);
            gifPaint.setFilterBitmap(true);
            gifPaint.setDither(true);
        }
        return gifPaint;
    }


    private void applyDrawToCanvas(PixelMap targetBitmap) {
        BITMAP_DRAWABLE_LOCK.lock();
        try {
            Canvas canvasRootShape = new Canvas(new Texture(targetBitmap));
            getImageElement().drawToCanvas(canvasRootShape);
            clear(canvasRootShape);
        } finally {
            BITMAP_DRAWABLE_LOCK.unlock();
        }
    }

    private static void clear(Canvas canvas) {
        canvas.setTexture(null);
    }

    private void setRectFloat(RectFloat rectFloat, int left, int top, int right, int bottom) {
        rectFloat.left = left;
        rectFloat.top = top;
        rectFloat.right = right;
        rectFloat.bottom = bottom;
    }


    private PixelMap getPixelMapFromResource(int resourceId) {
        InputStream inputStream = null;
        try {
            // 创建图像数据源ImageSource对象
            inputStream = getContext().getResourceManager().getResource(resourceId);
            ImageSource.SourceOptions srcOpts = new ImageSource.SourceOptions();
            srcOpts.formatHint = "image/jpg";
            ImageSource imageSource = ImageSource.create(inputStream, srcOpts);
            // 设置图片参数
            ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
            return imageSource.createPixelmap(decodingOptions);
        } catch (IOException | NotExistException e) {
            LogUtil.info(TAG, e.getMessage());
        }finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    LogUtil.info(TAG, e.getMessage());
                }
            }
        }
        return null;
    }


    @Override
    public boolean isBoundToWindow() {
        onAttach();
        return super.isBoundToWindow();
    }

    @Override
    public void onComponentBoundToWindow(Component component) {
        // LogUtil.info("doGif","onComponentBoundToWindow");
        onAttach();

    }

    @Override
    public void onComponentUnboundFromWindow(Component component) {
        // LogUtil.info("doGif","onComponentUnboundFromWindow");
        onDetach();

    }

    /**
     * Called by the system to attach. Subclasses may override.
     */
    protected void onAttach() {
        doAttach();
    }

    /**
     * Called by the system to detach. Subclasses may override.
     */
    protected void onDetach() {
        doDetach();
    }

    /**
     * Does the actual work of attaching.
     *
     * <p>Non-test subclasses should NOT override. Use onAttach for custom code.
     */
    protected void doAttach() {
        startDraweeViewGif();
    }

    private boolean hasGif() {
        return getImageElement() != null && getImageElement() instanceof RootShapeElement;
    }

    /**
     * Does the actual work of detaching.
     *
     * <p>Non-test subclasses should NOT override. Use onDetach for custom code.
     */
    protected void doDetach() {
        stopDraweeViewGif();
    }

    /**
     * 继承重写手势方案
     *
     * @param component 当前控件
     * @param event 触摸事件
     * @return 是否消费事件 此处等待用户自行扩展
     */
    public boolean onTouchEvent(Component component, TouchEvent event) {
        return false;
    }

    @Override
    public void invalidateDrawable(Element who) {
        onChange(who);
    }

    @Override
    public void scheduleDrawable(Element who, Runnable what, long when) {
    }

    @Override
    public void unscheduleDrawable(Element who, Runnable what) {
        MainHandler.removeTask(what);
    }

    private ScaleMode mScaleType = ScaleMode.ZOOM_CENTER;

    /**
     *  setScaleType
     * @param scaleType ScaleMode
     */
    public void setScaleType(ScaleMode scaleType) {
        if (scaleType != mScaleType) {
            mScaleType = scaleType;
            update();
        }
    }

    private void update() {
        invalidate();
    }

    private RectFloat scaleTypeFixed(PixelMap dstPixelMap,Component component) {
        Size size = dstPixelMap.getImageInfo().size;
        float top;
        float left;
        float right;
        float bottom;

        final int dwidth = size.width;
        final int dheight = size.height;

        final int vwidth = component.getWidth() - component.getPaddingLeft() - component.getPaddingRight();
        final int vheight = component.getHeight() - component.getPaddingTop() - component.getPaddingBottom();

        // LogUtil.info("doGif", "dwidth w =" +dwidth+ " dheight h =" + dheight+" vwidth w =" + vwidth+ " vheight h =" + vheight);

        if (mScaleType == ScaleMode.CENTER || mScaleType == ScaleMode.ZOOM_CENTER) {
            if (vwidth * 1.0f / dwidth > vheight * 1.0f / dheight) {
                left = (vwidth - dwidth * vheight / dheight) / 2f;
                right = vwidth - left;
                top = 0;
                bottom = vheight;
            } else {
                left = 0;
                right = vwidth;
                top = (vheight - dheight * vwidth / dwidth) / 2f;
                bottom = vheight - top;
            }
        } else if (mScaleType == ScaleMode.STRETCH) {
            left = 0;
            right = vwidth;
            top = 0;
            bottom = vheight;
        } else if (mScaleType == ScaleMode.ZOOM_END) {
            left = vwidth - dwidth;
            right = vwidth;
            top = vheight - dheight;
            bottom = vheight;
        } else if (mScaleType == ScaleMode.CLIP_CENTER) {
            float scale;
            float dx = 0, dy = 0;

            if (dwidth * vheight > vwidth * dheight) {
                scale = (float) vheight / (float) dheight;
                dx = (vwidth - dwidth * scale) * 0.5f;
            } else {
                scale = (float) vwidth / (float) dwidth;
                dy = (vheight - dheight * scale) * 0.5f;
            }
            RectFloat r = new RectFloat(0, 0, dwidth, dheight);
            Matrix matrix = new Matrix();
            matrix.setScale(scale, scale);
            matrix.postTranslate(Math.round(dx), Math.round(dy));
            matrix.mapRect(r);
            left = r.left;
            right = r.right;
            top = r.top;
            bottom = r.bottom;
        } else if (mScaleType == ScaleMode.INSIDE) {
            float scale;
            float dx;
            float dy;

            if (dwidth <= vwidth && dheight <= vheight) {
                scale = 1.0f;
            } else {
                scale = Math.min((float) vwidth / (float) dwidth, (float) vheight / (float) dheight);
            }

            dx = Math.round((vwidth - dwidth * scale) * 0.5f);
            dy = Math.round((vheight - dheight * scale) * 0.5f);
            RectFloat r = new RectFloat(0, 0, dwidth, dheight);
            Matrix matrix = new Matrix();
            matrix.setScale(scale, scale);
            matrix.postTranslate(dx, dy);
            matrix.mapRect(r);
            left = r.left;
            right = r.right;
            top = r.top;
            bottom = r.bottom;
        } else {
            left = 0;
            right = dwidth;
            top = 0;
            bottom = dheight;
        }
        RectFloat dst = new RectFloat(left, top, right, bottom);
        return dst;
    }

    private void startDraweeViewGif(){
        if(hasGif() && !userStopFlag){
            ((RootShapeElement) getImageElement()).onStartDraweeView();
        }
    }

    private void stopDraweeViewGif(){
        if(hasGif()){
            ((RootShapeElement) getImageElement()).onStopDraweeView();
        }
    }

    private void destoryDraweeViewGif(){
        if(hasGif()){
            ((RootShapeElement) getImageElement()).onDestroyDraweeView();
        }
    }

    private boolean userStopFlag = false;

    /**
     * 主动调用停止播放gif
     */
    public void stopGif(){
        userStopFlag = true;
        stopDraweeViewGif();
    }

    /**
     * 主动调用开始播放gif
     */
    public void startGif(){
        userStopFlag = false;
        startDraweeViewGif();
    }
}
